package gui;

import audio.WavWriter;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Cursor;
import java.awt.Graphics;
import java.awt.Rectangle;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import javax.swing.JPanel;

/**
 * This panel draws the lines, selections, and units; and contains the actual GraphJComponent graph
 * @author Peter Schilling, Jeff Hunter
 */
public class GraphPanel extends JPanel implements MouseListener, MouseMotionListener {

	private GraphJComponent graph_;
	private boolean isBarGraph_;
	private int startSelection_ = 0,  endSelection_ = 0;
	private Color backgroundColor_;
	private Color selectColor_;
	private Color lineColor_;
	private Color markerLineColor_;
	private WavWriter wavWriter_;
	private boolean isDragging_;

	GraphPanel(boolean isBarGraph) {
		startSelection_ = 0;
		endSelection_ = 0;
		backgroundColor_ = new Color(0, 0, 0);
		selectColor_ = new Color(40, 40, 40);
		lineColor_ = new Color(255, 0, 0);
		markerLineColor_ = new Color(25, 25, 25);
		graph_ = new GraphJComponent(isBarGraph);
		isBarGraph_ = isBarGraph;
		isDragging_ = false;
		setLayout(new BorderLayout());
		setBackground(backgroundColor_);
		addMouseListener(this);
		addMouseMotionListener(this);
		add(graph_);
	}

	public void addWavWriter(WavWriter ww) {
		wavWriter_ = ww;
	}

	@Override
	public void paintComponent(Graphics g) {
		super.paintComponent(g);
		if (!isBarGraph_) {
			g.setColor(selectColor_);
			if (startSelection_ <= endSelection_)
				g.fillRect(startSelection_, 0, endSelection_ - startSelection_, getSize().height); // fill the rectangle if positive
			else
				g.fillRect(endSelection_, 0, startSelection_ - endSelection_, getSize().height); // fill the rectangle if negative
			g.setColor(lineColor_);
			g.drawLine(startSelection_, 0, startSelection_, getSize().height); // vertical line showing start of selection
			g.drawLine(endSelection_, 0, endSelection_, getSize().height); // vertical line showing end of selection

			int sr = wavWriter_.getVisualSampleRate();
			for (int i = (int)(sr * graph_.getXSpace()), j = 1; i < this.getSize().width; i += sr * graph_.getXSpace(), j++){
				g.setColor(markerLineColor_);
				g.drawLine(i, 0, i, getSize().height);
				String secs = j + " seconds";
				g.setColor(new Color(50, 150, 50));
				g.drawChars(secs.toCharArray(), 0, secs.length(), i + 3, 18);
			}

			g.setColor(Color.lightGray);
			int zeroPosition = (int) (((double) getSize().height / (graph_.getMax() + Math.abs(graph_.getMin()))) * (double) graph_.getMax());
			g.drawLine(0, zeroPosition, getSize().width, zeroPosition); //horizontal line through center
			g.drawChars(new char[]{'0'}, 0, 1, 5, zeroPosition - 2); // '0' above line

			String max = "Max " + graph_.getMax();
			String min = "Min " + graph_.getMin();
			g.drawChars(max.toCharArray(), 0, max.length(), 5, 18); // max 
			g.drawChars(min.toCharArray(), 0, min.length(), 5, getSize().height - 4); // min
		}
		if (isBarGraph_) {
			g.setColor(new Color(50, 10, 10));

			if (startSelection_ <= endSelection_) {
				g.fillRect(startSelection_, 0, endSelection_ - startSelection_, getSize().height); // fill the rectangle if positive
				g.fillRect(getSize().width - endSelection_, 0, endSelection_ - startSelection_, getSize().height);
			} else {
				g.fillRect(endSelection_, 0, startSelection_ - endSelection_, getSize().height); // fill the rectangle if negative
				g.fillRect(getSize().width - startSelection_, 0, startSelection_ - endSelection_, getSize().height);
			}
			g.setColor(Color.blue);
			g.drawLine(getSize().width / 2, getSize().height, getSize().width / 2, 0); //verticle line showing nyquist limit
			if (wavWriter_ != null) {
				int samr = wavWriter_.getSampleRate();
				g.setColor(new Color(130,130,130));
				String windowMode = "Windowing Mode: " + wavWriter_.getWindowMode();
				String filterSize = "Filter size: " + wavWriter_.getNumBins();
				String freq = "Frequency: " + samr + " Hz";
				g.drawChars(windowMode.toCharArray(), 0, windowMode.length(), 60, 18);
				g.drawChars(filterSize.toCharArray(), 0, filterSize.length(), 60, 34);
				g.drawChars(freq.toCharArray(), 0, freq.length(), 60, 52);
				String start = "0 Hz";
				String nl = samr / 2 + " Hz";
				String end = samr + " Hz";
				g.setColor(new Color(150, 30, 30));
				g.drawChars(start.toCharArray(), 0, start.length(), 20, 18);
				g.drawChars(nl.toCharArray(), 0, nl.length(), this.getSize().width / 2 + 5, 18);
				g.drawChars(end.toCharArray(), 0, end.length(), this.getSize().width - 80, 18);
			}
		}

	}

	public void scrollToEnd() {
		graph_.scrollToEnd();
	}

	/**
	 * copy and delete points in selection
	 * @return copied points
	 */
	public byte[] cut() {
		if (isBarGraph_)
			return null;
		byte[] tempBytes;
		if (startSelection_ < endSelection_)
			tempBytes = wavWriter_.cut(adjustBounds(startSelection_), adjustBounds(endSelection_), graph_.getXSpace() * graph_.getPoints().length);
		else
			tempBytes = wavWriter_.cut(adjustBounds(endSelection_), adjustBounds(startSelection_), graph_.getXSpace() * graph_.getPoints().length);
		endSelection_ = startSelection_;
		return tempBytes;
	}

	/**
	 * copy points in selection
	 * @return copied points
	 */
	public byte[] copy() {
		if (isBarGraph_)
			return null;
		if (startSelection_ < endSelection_)
			return wavWriter_.copy(adjustBounds(startSelection_), adjustBounds(endSelection_), graph_.getXSpace() * graph_.getPoints().length);
		else
			return wavWriter_.copy(adjustBounds(endSelection_), adjustBounds(startSelection_), graph_.getXSpace() * graph_.getPoints().length);
	}

	/**
	 * paste points at position
	 * @param pointsToAdd points to add to graph
	 */
	public void paste(byte[] pasteBytes) {
		if (isBarGraph_)
			return;
		if (startSelection_ < endSelection_)
			wavWriter_.paste(pasteBytes, adjustBounds(startSelection_), graph_.getXSpace() * graph_.getPoints().length);
		else
			wavWriter_.paste(pasteBytes, adjustBounds(endSelection_), graph_.getXSpace() * graph_.getPoints().length);
	}

	/**
	 * delete all points in selection
	 */
	public void delete() {
		if (isBarGraph_)
			return;
		if (startSelection_ < endSelection_)
			wavWriter_.delete(adjustBounds(startSelection_), adjustBounds(endSelection_), graph_.getXSpace() * graph_.getPoints().length);
		else
			wavWriter_.delete(adjustBounds(endSelection_), adjustBounds(startSelection_), graph_.getXSpace() * graph_.getPoints().length);
		endSelection_ = startSelection_;
	}
	
	public void fourier() {
		if (isBarGraph_)
			return;
		if (startSelection_ < endSelection_)
			wavWriter_.updateFreqDomain(adjustBounds(startSelection_), adjustBounds(endSelection_), graph_.getXSpace() * graph_.getPoints().length);
		else
			wavWriter_.updateFreqDomain(adjustBounds(endSelection_), adjustBounds(startSelection_), graph_.getXSpace() * graph_.getPoints().length);
		repaint();
	}

	/**
	 * add a point to the graph component
	 * @param p point
	 */
	public void addPoint(double p) {
		graph_.addPoint(p);
	}

	/**
	 * add a set of points to the graph component
	 * @param p point
	 */
	public void addPoints(double[] aP) {
		for (int i = 0; i < aP.length; i++)
			graph_.addPoint(aP[i]);
	}

	/**
	 * add a set of points to the graph component
	 * @param p point
	 */
	public void addPoints(int[] aP) {
		for (int i = 0; i < aP.length; i++)
			graph_.addPoint((double) aP[i]);
	}

	public void updateGraph() {
		//graph_.repaint();
		updateGraph();
	}

	/**
	 * fetch the graph's points
	 * @return points in graph
	 */
	public double[] getPoints() {
		return graph_.getPoints();
	}

	/**
	 * delete all points in graph
	 */
	public void deleteAll() {
		graph_.deleteAll();
	}

	/**
	 * draw a line on mouse press
	 * @param arg0
	 */
	public void mouseClicked(MouseEvent arg0) {
		if (arg0.getButton() == 1) {
			startSelection_ = arg0.getX();
			endSelection_ = arg0.getX();
			if (startSelection_ < 0)
				startSelection_ = 0;
			if (endSelection_ < 0)
				endSelection_ = 0;
		}
		if (arg0.getButton() == 3)
			if (!isBarGraph_)
				wavWriter_.updateFreqDomain(adjustBounds(arg0.getX() - 20), adjustBounds(arg0.getX() + 20), graph_.getXSpace() * graph_.getPoints().length);
		repaint();
	}

	/**
	 * start selection on mouse press
	 * @param arg0
	 */
	public void mousePressed(MouseEvent arg0) {
		if (arg0.getButton() == 1) {
			startSelection_ = arg0.getX();
			endSelection_ = arg0.getX();
			if (startSelection_ < 0)
				startSelection_ = 0;
			if (endSelection_ < 0)
				endSelection_ = 0;
			isDragging_ = true;
		}
		repaint();
	}

	/**
	 * end the selection on mouse release
	 * @param arg0
	 */
	public void mouseReleased(MouseEvent arg0) {
		if (arg0.getButton() == 1) {
			if (arg0.getX() > startSelection_)
				endSelection_ = arg0.getX();
			else {
				endSelection_ = startSelection_;
				startSelection_ = arg0.getX();
			}
			if (startSelection_ < 0)
				startSelection_ = 0;
			if (endSelection_ < 0)
				endSelection_ = 0;
			isDragging_ = false;
		}
		repaint();
	}

	/**
	 * update selection on mouse drag
	 * @param arg0
	 */
	public void mouseDragged(MouseEvent arg0) {
		if (isDragging_) {
			Rectangle vrec = this.getVisibleRect();
			if(arg0.getX() > vrec.x + vrec.width)
				this.scrollRectToVisible(new Rectangle(vrec.x + 10, vrec.y, vrec.width, vrec.height));	
			if(arg0.getX() < vrec.x)
				this.scrollRectToVisible(new Rectangle(vrec.x - 10, vrec.y, vrec.width, vrec.height));	
			endSelection_ = arg0.getX();
			if (endSelection_ < 0)
				endSelection_ = 0;
		}
		repaint();
	}

	/**
	 * show a text-style cursor on mouse enter
	 * @param arg0
	 */
	public void mouseEntered(MouseEvent arg0) {
		this.setCursor(new Cursor(Cursor.TEXT_CURSOR));
	}

	public void mouseExited(MouseEvent arg0) {
		// not necessary...
	}

	public void mouseMoved(MouseEvent arg0) {
		// not necessary...
	}

	private int adjustBounds(int selection) {
		if (selection < 0)
			return 0;
		if ((double) selection > (graph_.getXSpace() * (double) graph_.getPoints().length))
			return (int) (graph_.getXSpace() * (double) graph_.getPoints().length);
		return selection;
	}

	/**
	 * Set the distance between points on graph, appearing as a zoom
	 * @param zoomPercent
	 */
	public void setZoom(int zoomPercent) {
		graph_.setXSpace((double) zoomPercent / 10.0);
	}

	public int getEndSelection() {
		return endSelection_;
	}

	public int getStartSelection() {
		return startSelection_;
	}
}
	
